home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / cgazv5n5.arc / PROFILER.C < prev    next >
C/C++ Source or Header  |  1991-09-23  |  33KB  |  1,113 lines

  1. /*--- PROFILER.C ---------------------- Listing 1 ---------
  2.  * OS/2 1.x Execution Profiler
  3.  * Author     : Mark Florence, San Francisco Canyon Co, Inc.
  4.  * History    : 01/27/91 Initial Implementation
  5.  * Compiler   : MS C6.00a (Large model)
  6.  *
  7.  * (c) C Gazette. Source may be used if authorship and publi-
  8.  * cation are acknowledged. Object files may be used freely.
  9.  *-------------------------------------------------------*/
  10.  
  11. #define INCL_DOS
  12. #define INCL_DOSTRACE
  13.  
  14. #include <os2.h>
  15. #include <dos.h>
  16. #include <string.h>
  17. #include <stdlib.h>
  18. #include <stdio.h>
  19. #include <io.h>
  20. #include <process.h>
  21. #include <malloc.h>
  22. #include <time.h>
  23.  
  24. #include "Profiler.h"
  25.  
  26. // Constants
  27.  
  28. #define SAMPLE_INTERVAL         10L   // Nominal sample interval
  29. #define STACK_SIZE            8192    // Stack size for threads
  30. #define MAX_SELECTORS         8192    // Maximum selectors
  31. #define NOMINAL_BUCKET_SIZE     64    // Nominal size of buckets
  32. #define MINIMUM_BUCKET_SIZE     32    // Minimum size of buckets
  33.  
  34. // DosPTrace Commands
  35.  
  36. // Get program's registers:
  37. #define PTRACE_CMD_READ_REGISTERS   0x0003 
  38.  
  39. // (Re)start program
  40. #define PTRACE_CMD_GO               0x0007 
  41.  
  42. // Stop program
  43. #define PTRACE_CMD_STOP             0x000A 
  44.  
  45. // Convert index to selector
  46. #define PTRACE_CMD_NUMTOSEL         0x000D 
  47.  
  48. // Get DLL name
  49. #define PTRACE_CMD_GET_LIBNAME      0x0010 
  50.  
  51. // DosPTrace Events
  52.  
  53. #define PTRACE_EVENT_SUCCESS       0  // Successful return
  54. #define PTRACE_EVENT_ERROR        -1  // Error (bad command etc)
  55. #define PTRACE_EVENT_SIGNAL       -2  // Breakpoint at signal
  56. #define PTRACE_EVENT_SINGLE_STEP  -3  // Single-step breakpoint   
  57. #define PTRACE_EVENT_BREAKPOINT   -4  // Breakpoint
  58. #define PTRACE_EVENT_DYING        -6  // Program ending
  59. #define PTRACE_EVENT_GP_FAULT     -7  // GP fault detected
  60. #define PTRACE_EVENT_LIBRARY_LOAD -8  // DLL loading
  61. #define PTRACE_EVENT_THREAD_DEAD  -10 // Thread ending
  62.  
  63. // Module (.EXE or .DLL) Function List 
  64. // (list of functions in a module)
  65.  
  66. typedef struct _FLIST         // Hungarian: flst, pflst
  67. {   USHORT selv, offv, lenv;  // Funct. selector, offset, length
  68.     PCHAR pszName;            // -->Function name
  69.     struct _FLIST *pflstNext; // -->Next function in list
  70. } FLIST, *PFLIST;
  71.  
  72. // Module (.EXE or .DLL) List 
  73. // (list of modules actually loaded)
  74.  
  75. typedef struct _MLIST         // Hungarian: mlst, pmlst
  76. {   PCHAR pszName;            // -->Module name
  77.     struct _MLIST *pmlstNext; // -->Next module in list
  78.     struct _FLIST *pflstFirst; //-->First function in list
  79. } MLIST, *PMLIST;
  80.  
  81. // Library (.DLL) List 
  82. // (list of DLLs for which profiling is required)
  83.  
  84. typedef struct _LLIST         // Hungarian: llst, pllst
  85. {   PCHAR pszName;            // -->DLL name 
  86.     struct _LLIST *pllstNext; // -->Next DLL in list
  87. }   LLIST, *PLLIST;
  88.  
  89. // Activity Buckets 
  90. // (one set for each segment in each module)
  91.  
  92. typedef struct _BUCKETS       // Hungarian: bckt, pbckt        
  93. {   USHORT segv;              // Segment                       
  94.     ULONG ulSegSize;          // Segment size                  
  95.     struct _MLIST *pmlst;     // -->Module                     
  96.     USHORT cBuckets;          // Bucket count                  
  97.     LONG alBucket[0];         // Array of buckets              
  98. }   BUCKETS, *PBUCKETS;
  99.  
  100. // Selector-to-Segment Index 
  101. // (one for each possible selector)
  102.  
  103. typedef struct _SELINDEX      // Hungarian: selix, pselix
  104. {   PBUCKETS apbckt[MAX_SELECTORS]; // -->Activity buckets
  105. }   SELINDEX, *PSELINDEX;
  106.  
  107. // Global Data
  108.  
  109. struct
  110. {   PTRACEBUF ptb;              // PTrace buffer
  111.     PSELINDEX pselix;           // -->Selector-to-segment index
  112.     PMLIST pmlstFirst;          // -->First module loaded
  113.     PLLIST pllstFirst;          // -->First DLL expected
  114.     BOOL fAllDLLs;              // TRUE == Profile ALL DLLs
  115.     USHORT cbBucket;            // Bucket size
  116.     LONG lUnknown, lKnown;      // Unknown, known activity count
  117.     CHAR szResults[CCHMAXPATH]; // Results file
  118. }   NEAR glbl;
  119.  
  120. // Function Declarations
  121.  
  122. VOID        main               (int, char **);
  123. VOID   NEAR BuildBuckets       (PMLIST);
  124. VOID   NEAR BuildFunctionList  (PMLIST, PCHAR);
  125. VOID   NEAR BuildLibraryList   (PCHAR);
  126. VOID   NEAR BuildModuleList    (PCHAR, BOOL);
  127. VOID   NEAR BuildSelectorIndex (VOID);
  128. int    NEAR ExtractOptions     (int, char **);
  129. VOID   NEAR FreeDataStructures (VOID);
  130. VOID   NEAR PrintResults       (VOID);
  131. USHORT NEAR ProfileProgram     (PCHAR);
  132. VOID   FAR  Signaller          (PVOID);
  133. VOID   NEAR StartProgram       (int, char **, int, PCHAR);
  134.  
  135. // Function: main - Profiler Main Function
  136.  
  137. VOID main (argc, argv)
  138. int argc;                       // Argument count
  139. char *argv[];                   // Argument values
  140.  
  141. // Define function data
  142. {
  143.       int iarg;                 // Index to program name
  144.       USHORT idErr;             // Error return code
  145.       CHAR szPgm[CCHMAXPATH];   // Name of program to profile
  146.       int tidSignaller;         // Signaller thread id
  147.  
  148.    // Extract options from arguments
  149.       iarg = ExtractOptions (argc, argv);
  150.  
  151.    // Start program to be profiled
  152.       StartProgram (argc, argv, iarg, szPgm);
  153.  
  154.    // Build selector-to-segment index
  155.       BuildSelectorIndex ();
  156.  
  157.    // Start signalling thread
  158.  
  159.       tidSignaller = _beginthread (Signaller, NULL, 
  160.                                    STACK_SIZE, NULL);
  161.       DosSetPrty (PRTYS_THREAD, PRTYC_TIMECRITICAL,
  162.                   PRTYD_MAXIMUM, tidSignaller);
  163.  
  164.    // Profile program
  165.       idErr = ProfileProgram (szPgm);
  166.  
  167.    // Print results
  168.       PrintResults ();
  169.  
  170.    // Free all data structures
  171.       FreeDataStructures ();
  172.  
  173.    // Return to caller
  174.       exit (idErr);
  175. }
  176.  
  177. // Build Accumulator Buckets for a Module
  178.  
  179. VOID NEAR BuildBuckets (pmlst)
  180.  
  181.       PMLIST pmlst;             // -->Module list entry
  182.  
  183.    // Define function data
  184.  
  185. {     PBUCKETS pbckt;           // -->Accumulator buckets
  186.       USHORT segv, selv;        // Segment and selector values
  187.       USHORT cBuckets;          // Count of buckets
  188.       USHORT iBucket;           // Bucket index
  189.       CHAR szCommand[CCHMAXPATH*2]; // EXEHDR command
  190.       BOOL fIgnore = TRUE;      // TRUE == ignoring EXEHDR line
  191.       CHAR szLine[256];         // Line from temporary file
  192.       ULONG ulSegSize;          // Segment size
  193.       CHAR szSegType[32];       // Segment type
  194.       FILE *fTempFile;          // Temporary file handle
  195.       CHAR szTempFile[L_tmpnam]; //Temporary file name
  196.  
  197.       static PCHAR pszHdr = 
  198.             "no. type address  file  mem   flags";
  199.  
  200.    // Issue EXEHDR command
  201.  
  202.       tmpnam (szTempFile);
  203.       sprintf (szCommand, "EXEHDR /NOLOGO %s >%s",
  204.         pmlst->pszName, szTempFile);
  205.       system (szCommand);
  206.  
  207.    // Open temporary file with EXEHDR results
  208.  
  209.       fTempFile = fopen (szTempFile, "rt");
  210.  
  211.    // Issue an error if the EXEHDR results could not be found
  212.  
  213.       if (fTempFile == 0)
  214.       {  printf 
  215.         ("\nWARNING: Unable to open EXEHDR temporary file %s\n",
  216.           szTempFile);
  217.          return;
  218.       }
  219.  
  220.    // Read temporary file with EXEHDR results 
  221.    // to build buckets for all CODE segments
  222.  
  223.       while (fgets (szLine, sizeof (szLine), fTempFile) != NULL)
  224.  
  225.    // Ignore EXEHDR lines until we get to the interesting part
  226.  
  227.       { if (fIgnore && (strstr (szLine, pszHdr) != NULL))
  228.            {fIgnore = FALSE;
  229.             continue;
  230.            }
  231.         if (fIgnore) 
  232.            continue;
  233.  
  234.       // Extract segment number and length from the EXEHDR line
  235.  
  236.         if (sscanf (szLine, "%u %s %*s %*s %lx",
  237.             &segv, szSegType, &ulSegSize) < 3)
  238.               break;
  239.  
  240.       // Ignore non-CODE segments
  241.  
  242.         if (strstr (szSegType, "CODE") == NULL)
  243.             continue;
  244.  
  245.       // Convert each segment to a selector
  246.  
  247.          glbl.ptb.cmd = PTRACE_CMD_NUMTOSEL;
  248.          glbl.ptb.value = segv;
  249.          DosPTrace ((PBYTE) &glbl.ptb);
  250.          if (glbl.ptb.cmd != 0)
  251.             continue;
  252.          selv = glbl.ptb.value;
  253.  
  254.       // Convert selector to an index [0..8191]
  255.  
  256.          selv >>= 3;
  257.  
  258.       // Calculate the number of buckets from the segment size
  259.  
  260.          cBuckets = (USHORT) (ulSegSize / glbl.cbBucket) + 1;
  261.  
  262.       // Allocate a block for the buckets
  263.  
  264.          pbckt = (PBUCKETS) malloc (sizeof (*pbckt)
  265.            + (cBuckets * sizeof (LONG)));
  266.  
  267.       // Issue an error if out of memory
  268.  
  269.          if (pbckt == NULL)
  270.            {printf 
  271.             ("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
  272.             exit (1);
  273.            }
  274.  
  275.       // Chain buckets to the selector-to-segment index
  276.  
  277.          glbl.pselix->apbckt[selv] = pbckt;
  278.  
  279.       // Initialize buckets
  280.  
  281.          pbckt->segv      = segv;
  282.          pbckt->ulSegSize = ulSegSize;
  283.          pbckt->pmlst     = pmlst;
  284.          pbckt->cBuckets  = cBuckets;
  285.  
  286.       // Loop until all temporary file lines read
  287.          for (iBucket = 0; iBucket < cBuckets; iBucket++)
  288.             pbckt->alBucket[iBucket] = 0;
  289.  
  290.         }
  291.  
  292.    // Close then erase temporary file
  293.  
  294.       fclose (fTempFile);
  295.       unlink (szTempFile);
  296. }
  297.  
  298. // Build List of Functions for a Module
  299.  
  300. VOID NEAR BuildFunctionList (pmlst, pszMap)
  301.  
  302.       PMLIST pmlst;             // -->Module list entry
  303.       PCHAR pszMap;             // -->Name of map file
  304.  
  305.    // Define function data
  306.  
  307. {  // Segment, selector, offset values:
  308.    USHORT segv, selv, offv; 
  309.    // Current, last function list entry:
  310.    PFLIST pflst, pflstLast = NULL;  
  311.    CHAR szFunction[CCHMAXPATH]; // Current function name
  312.    BOOL fIgnore = TRUE;         // TRUE == ignoring MAP line
  313.    CHAR szLine[256];            // Line from MAP file
  314.    FILE *fMap;                  // MAP file handle
  315.  
  316.    static PCHAR pszHdr = "Publics by Value";
  317.  
  318.    // Open map file
  319.  
  320.       fMap = fopen (pszMap, "rt");
  321.  
  322.    // Issue an error if the MAP file could not be found
  323.  
  324.       if (fMap == 0)
  325.         {printf 
  326.            ("\nWARNING: Unable to open MAP file %s\n", pszMap);
  327.          return;
  328.         }
  329.  
  330.    // Read MAP file to build FLIST entries for each function
  331.  
  332.       while (fgets (szLine, sizeof (szLine), fMap) != NULL)
  333.  
  334.       // Ignore MAP lines until we get to the interesting part
  335.  
  336.         {if (fIgnore && (strstr (szLine, pszHdr) != NULL))
  337.            {fIgnore = FALSE;
  338.             if (fgets (szLine, sizeof (szLine), fMap) == NULL)
  339.                break;
  340.             else continue;
  341.            }
  342.          if (fIgnore) 
  343.             continue;
  344.  
  345.       // Extract function details from the MAP line
  346.  
  347.          if (sscanf (szLine, "%4x %*c %4x %s", &segv, &offv,
  348.            szFunction) < 3)
  349.             break;
  350.  
  351.       // Ignore non-CODE functions
  352.  
  353.          glbl.ptb.cmd = PTRACE_CMD_NUMTOSEL;
  354.          glbl.ptb.value = segv;
  355.          DosPTrace ((PBYTE) &glbl.ptb);
  356.          if (glbl.ptb.cmd != 0)
  357.             continue;
  358.          selv = glbl.ptb.value >> 3;
  359.          if (glbl.pselix->apbckt[selv] == NULL)
  360.             continue;
  361.  
  362.       // Allocate a block for the function list entry
  363.  
  364.          pflst = (PFLIST) malloc (sizeof (*pflst));
  365.  
  366.       // Issue an error if out of memory
  367.  
  368.          if (pflst == NULL)
  369.            {printf 
  370.             ("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
  371.             exit (1);
  372.            }
  373.  
  374.       // Allocate a block for the function name
  375.  
  376.          pflst->pszName = 
  377.             (PCHAR) malloc (strlen (szFunction) + 1);
  378.  
  379.       // Issue an error if out of memory
  380.  
  381.          if (pflst->pszName == NULL)
  382.            {printf (
  383.             "\nOut of memory at %s:%d\n", __FILE__, __LINE__);
  384.             exit (1);
  385.            }
  386.  
  387.       // Initialize function list entry
  388.  
  389.          pflst->selv = selv;
  390.          pflst->offv = offv;
  391.          if (pflstLast != NULL)
  392.            {if (pflst->selv == pflstLast->selv)
  393.                pflstLast->lenv = pflst->offv - pflstLast->offv;
  394.             else pflstLast->lenv = (USHORT)
  395.                (glbl.pselix->apbckt[pflstLast->selv]->ulSegSize
  396.               - pflstLast->offv);
  397.            }
  398.          strcpy (pflst->pszName, szFunction);
  399.          pflstLast = pflst;
  400.  
  401.       // Link this entry to the function list chain
  402.  
  403.          if (pmlst->pflstFirst == NULL)
  404.            {pmlst->pflstFirst = pflst;
  405.             pflst->pflstNext = NULL;
  406.            }
  407.          else
  408.            {pflst->pflstNext = pmlst->pflstFirst;
  409.             pmlst->pflstFirst = pflst;
  410.            }
  411.  
  412.       // Loop until all MAP lines read
  413.  
  414.         }
  415.  
  416.    // Set size of last function, if any
  417.  
  418.       if (pflstLast != NULL)
  419.          pflstLast->lenv = (USHORT)
  420.            (glbl.pselix->apbckt[pflstLast->selv]->ulSegSize
  421.            - pflstLast->offv);
  422.  
  423.    // Close the MAP file
  424.  
  425.       fclose (fMap);
  426. }
  427.  
  428. // Add Entry to Library List
  429.  
  430. VOID NEAR BuildLibraryList (pszModule)
  431.  
  432.       PCHAR pszModule;          // -->DLL name
  433.  
  434.    // Define function data
  435.  
  436. {     PLLIST pllst;             // -->Library list entry
  437.  
  438.    // Check if ALL specified
  439.  
  440.       if (stricmp (pszModule, "ALL") == 0)
  441.         {glbl.fAllDLLs = TRUE;
  442.          return;
  443.         }
  444.  
  445.    // Allocate a block for the library list entry
  446.  
  447.       pllst = (PLLIST) malloc (sizeof (*pllst));
  448.  
  449.    // Issue an error if out of memory
  450.  
  451.       if (pllst == NULL)
  452.         {printf 
  453.           ("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
  454.          exit (1);
  455.         }
  456.  
  457.    // Allocate a block for the library name
  458.  
  459.       pllst->pszName = 
  460.             (PCHAR) malloc (strlen (pszModule) + 1);
  461.  
  462.    // Issue an error if out of memory
  463.  
  464.       if (pllst->pszName == NULL)
  465.         {printf 
  466.           ("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
  467.          exit (1);
  468.         }
  469.  
  470.    // Initialize the library list entry
  471.  
  472.       strcpy (pllst->pszName, pszModule);
  473.       strupr (pllst->pszName);
  474.       pllst->pllstNext = NULL;
  475.  
  476.    // Link this entry to the library list chain
  477.  
  478.       if (glbl.pllstFirst == NULL)
  479.          glbl.pllstFirst = pllst;
  480.       else
  481.         {pllst->pllstNext = glbl.pllstFirst;
  482.          glbl.pllstFirst = pllst;
  483.         }
  484. }
  485.  
  486. //  Add Entry to Module List
  487.  
  488. VOID NEAR BuildModuleList (pszModule, fLibrary)
  489.  
  490.       PCHAR pszModule;          // -->DLL or EXE name
  491.       BOOL fLibrary;            // TRUE == DLL
  492.  
  493.    // Define function data
  494.  
  495. {     PMLIST pmlst;             // -->Module list entry
  496.       PLLIST pllst;             // -->Library list entry
  497.       FILESTATUS fsts;          // File status information
  498.       PCHAR pszDLL;             // -->DLL in file name
  499.       PCHAR pszDot;             // -->Dot in file name
  500.       CHAR szMap[CCHMAXPATH];   // Map file name
  501.       BOOL fMatch = FALSE;      // TRUE == library name matches
  502.  
  503.    // Ignore libraries (.DLLs) that are not expected
  504.  
  505.       if (fLibrary && !glbl.fAllDLLs)
  506.         {pllst = glbl.pllstFirst;
  507.          while (pllst != NULL)
  508.            {if ((pszDLL = strrchr (pszModule, '\\')) != NULL)
  509.                pszDLL++;
  510.             else pszDLL = pszModule;
  511.             if (strstr (pszDLL, pllst->pszName) != NULL)
  512.               {fMatch = TRUE;
  513.                break;
  514.               }
  515.             pllst = pllst->pllstNext;
  516.            }
  517.          if (! fMatch) return;
  518.         }
  519.  
  520.    // Allocate a block for the module list entry
  521.  
  522.       pmlst = (PMLIST) malloc (sizeof (*pmlst));
  523.  
  524.    // Issue an error if out of memory
  525.  
  526.       if (pmlst == NULL)
  527.         {printf 
  528.           ("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
  529.          exit (1);
  530.         }
  531.  
  532.    // Allocate a block for the module name
  533.  
  534.       pmlst->pszName = (PCHAR) malloc (strlen (pszModule) + 1);
  535.  
  536.    // Issue an error if out of memory
  537.  
  538.       if (pmlst->pszName == NULL)
  539.         {printf 
  540.           ("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
  541.          exit (1);
  542.         }
  543.  
  544.    // Initialize the module list entry
  545.  
  546.       strcpy (pmlst->pszName, pszModule);
  547.       strupr (pmlst->pszName);
  548.       pmlst->pmlstNext = NULL;
  549.       pmlst->pflstFirst = NULL;
  550.  
  551.    // Link this entry to the module list chain
  552.  
  553.       if (glbl.pmlstFirst == NULL)
  554.          glbl.pmlstFirst = pmlst;
  555.       else
  556.         {pmlst->pmlstNext = glbl.pmlstFirst;
  557.          glbl.pmlstFirst = pmlst;
  558.         }
  559.  
  560.    // Build accumulator buckets for this module
  561.  
  562.       BuildBuckets (pmlst);
  563.  
  564.    // If there's a map file, build the 
  565.    // function list for this module
  566.  
  567.       strcpy (szMap, pmlst->pszName);
  568.       if ((pszDot = strrchr (szMap, '.')) == NULL)
  569.          strcat (szMap, ".MAP");
  570.       else strcpy (pszDot, ".MAP");
  571.       if (DosQPathInfo (szMap, FIL_STANDARD,
  572.         (PBYTE) &fsts, sizeof (fsts), 0L) == 0)
  573.          BuildFunctionList (pmlst, szMap);
  574. }
  575.  
  576. // Build Selector-to-Segment Index
  577.  
  578. VOID NEAR BuildSelectorIndex (VOID)
  579.  
  580.    // Define function data
  581.  
  582. {  USHORT iselix;               // Index into selector table
  583.  
  584.    // Allocate a selector-to-segment index
  585.  
  586.       glbl.pselix = (PSELINDEX) malloc (sizeof (*glbl.pselix));
  587.  
  588.    // Issue an error if out of memory
  589.  
  590.       if (glbl.pselix == NULL)
  591.         {printf 
  592.            ("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
  593.          exit (1);
  594.         }
  595.  
  596.    // Initialize selector-to-segment index
  597.  
  598.       for (iselix = 0; iselix < MAX_SELECTORS; iselix++)
  599.          glbl.pselix->apbckt[iselix] = NULL;
  600. }
  601.  
  602. // Extract Options from Command Line
  603.  
  604.    int NEAR ExtractOptions (argc, argv)
  605.  
  606.       int argc;                 // Argument count
  607.       char *argv[];             // Argument values
  608.  
  609.    // Define function data
  610.  
  611.      {int iarg;                 // First non-option argument
  612.       FILESTATUS fsts;          // File status information
  613.       USHORT cbBucket;          // Bucket size
  614.  
  615.    // Verify arguments
  616.  
  617.       if (argc < 2)
  618.         {printf 
  619.           ("\nUsage is: %s [<options>] <program> [<args>]\n",
  620.            argv[0]);
  621.          printf ("<options> are:\n");
  622.          printf
  623.          ("-B:nn           -- size of accumulator buckets\n");
  624.          printf
  625.          ("-L:xxxxxxxx.xxx -- DLL to be profiled\n");
  626.          printf
  627.          ("                   If ALL, all DLLs profiled\n");
  628.          printf
  629.          ("                   May be repeated as necessary\n");
  630.          printf
  631.          ("-O:xxxxxxxx.xxx -- output file for profile data\n");
  632.          printf ("<program> is program to be profiled\n");
  633.          printf ("<args> are its arguments, if any\n");
  634.          exit (1);
  635.         }
  636.  
  637.    // Set default argument values
  638.  
  639.       glbl.cbBucket = NOMINAL_BUCKET_SIZE;
  640.       strcpy (glbl.szResults, "NUL");
  641.  
  642.    // Scan arguments looking for options
  643.  
  644.       for (iarg = 1; iarg < argc; iarg++)
  645.  
  646.       // Stop if argument is not an option
  647.  
  648.         {if ((argv[iarg][0] != '-') && 
  649.              (argv[iarg][0] != '/')) 
  650.                break;
  651.          if (strlen (argv[iarg]) < 4) 
  652.                break;
  653.          if (argv[iarg][2] != ':') 
  654.                break;
  655.  
  656.       // Process option
  657.  
  658.          switch (argv[iarg][1])
  659.  
  660.          // -B:nn --- Bucket size
  661.  
  662.            {case 'b':
  663.             case 'B':
  664.                cbBucket = atoi (&(argv[iarg][3]));
  665.                if (cbBucket < MINIMUM_BUCKET_SIZE)
  666.                   printf 
  667.                     ("\nWARNING: Bucket size must be >= %d\n",
  668.                     MINIMUM_BUCKET_SIZE);
  669.                else glbl.cbBucket = cbBucket;
  670.                break;
  671.  
  672.          // -L:xxxxxxxx.xxx --- DLL name
  673.  
  674.             case 'l':
  675.             case 'L':
  676.                if (DosQPathInfo (&(argv[iarg][3]), 
  677.                    FIL_NAMEISVALID, (PBYTE) &fsts, 
  678.                    sizeof (fsts), 0L) != 0)
  679.                   printf 
  680.                     ("\nWARNING: %s is an invalid DLL name\n",
  681.                        &(argv[iarg][3]));
  682.                else 
  683.                   BuildLibraryList (&(argv[iarg][3]));
  684.                break;
  685.  
  686.          // -O:xxxxxxxx.xxx --- Output file name
  687.  
  688.             case 'o':
  689.             case 'O':
  690.                if (DosQPathInfo (&(argv[iarg][3]), 
  691.                    FIL_NAMEISVALID, (PBYTE) &fsts, 
  692.                    sizeof (fsts), 0L) != 0)
  693.                   printf 
  694.                     ("\nWARNING: %s is an invalid file name\n",
  695.                        &(argv[iarg][3]));
  696.                else 
  697.                   strcpy (glbl.szResults, &(argv[iarg][3]));
  698.                break;
  699.  
  700.          // Unknown option
  701.  
  702.             default:
  703.                printf 
  704.                ("\nWARNING: %s is invalid and is ignored\n",
  705.                  argv[iarg]);
  706.                break;
  707.  
  708.            }
  709.         }
  710.  
  711.       return iarg;
  712. }
  713.  
  714.  
  715. //  Free All Acquired Data Structures
  716.  
  717. VOID NEAR FreeDataStructures (VOID)
  718.  
  719.    // Define function data
  720.  
  721. {     USHORT iselix;            // Index to selectors
  722.       PLLIST pllst, pllstX;     // -->DLL list entry
  723.       PMLIST pmlst, pmlstX;     // -->Module list entry
  724.       PFLIST pflst, pflstX;     // -->Function list entry
  725.  
  726.    // Free all accumulator buckets and 
  727.    // the selector-to-segment index
  728.  
  729.       for (iselix = 0; iselix < MAX_SELECTORS; iselix++)
  730.         {if (glbl.pselix->apbckt[iselix] != NULL)
  731.             free (glbl.pselix->apbckt[iselix]);
  732.         }
  733.       free (glbl.pselix);
  734.  
  735.    // Free each module list entry and 
  736.    // its associated function list entries
  737.  
  738.       pmlst = glbl.pmlstFirst;
  739.       while (pmlst != NULL)
  740.         {pflst = pmlst->pflstFirst;
  741.          while (pflst != NULL)
  742.            {pflstX = pflst;
  743.             pflst = pflst->pflstNext;
  744.             free (pflstX->pszName);
  745.             free (pflstX);
  746.            }
  747.          pmlstX = pmlst;
  748.          pmlst = pmlst->pmlstNext;
  749.          free (pmlstX->pszName);
  750.          free (pmlstX);
  751.         }
  752.  
  753.    // Free list of expected DLLs
  754.  
  755.       pllst = glbl.pllstFirst;
  756.       while (pmlst != NULL)
  757.         {pllstX = pllst;
  758.          pllst = pllst->pllstNext;
  759.          free (pllstX->pszName);
  760.          free (pllstX);
  761.         }
  762. }
  763.  
  764. //  Output Profiling Results
  765.  
  766. VOID NEAR PrintResults (VOID)
  767.  
  768.    // Define function data
  769.  
  770.      {USHORT iselix;            // Index into selector table
  771.       PBUCKETS pbckt;           // -->Accumulator buckets
  772.       USHORT offv;              // Precomputed offset value
  773.       PMLIST pmlst;             // -->Module list entry
  774.       PFLIST pflst;             // -->Function list entry
  775.       USHORT iBucket;           // Index into buckets
  776.       CHAR szFunction[CCHMAXPATH]; // Function name
  777.       PCHAR pszName;            // -->Actual module name
  778.       FILE *fResults;           // Output results file
  779.  
  780.    // Open file to hold results
  781.  
  782.       fResults = fopen (glbl.szResults, "wt");
  783.  
  784.    // Issue an error if the results file could not be opened
  785.  
  786.       if (fResults == 0)
  787.         {printf 
  788.         ("\nWARNING: Unable to open output results file %s\n",
  789.            glbl.szResults);
  790.          return;
  791.         }
  792.  
  793.    // Output total unknown, known counts
  794.  
  795.       fprintf (fResults, "Unknown, Known\n");
  796.       fprintf (fResults, "%ld, %ld\n", 
  797.                            glbl.lUnknown, glbl.lKnown);
  798.  
  799.    // Leave room for expansion. If new output is required, 
  800.    // we will replace the following dummy lines.
  801.  
  802.    fprintf (fResults, "This line is reserved for future use\n");
  803.    fprintf (fResults, "This line is reserved for future use\n");
  804.  
  805.    // For each selector that is in use...
  806.  
  807.       fprintf (fResults, 
  808.            "Module, SEG:OFF, Count, Module, Function, Count\n");
  809.       for (iselix = 0; iselix < MAX_SELECTORS; iselix++)
  810.         {if (glbl.pselix->apbckt[iselix] != NULL)
  811.  
  812.          // For each non-zero accumulator bucket...
  813.  
  814.            {pbckt = glbl.pselix->apbckt[iselix];
  815.             for (iBucket = 0; iBucket < pbckt->cBuckets; 
  816.                 iBucket++)
  817.               {if (pbckt->alBucket[iBucket] != 0)
  818.  
  819.                // Initialize function as module name in case we
  820.                // don't have access to any function information
  821.  
  822.                  {offv = iBucket * glbl.cbBucket;
  823.                   if ((pszName = strrchr (
  824.                         pbckt->pmlst->pszName, '\\')) != NULL)
  825.                      pszName++;
  826.                   else 
  827.                      pszName = pbckt->pmlst->pszName;
  828.  
  829.                   sprintf (szFunction, "%s[%04X:%04X]",
  830.                     pszName, pbckt->segv, offv);
  831.  
  832.                // Scan each function in each module to see if
  833.                // this accumulator bucket lies within it
  834.  
  835.                   pmlst = glbl.pmlstFirst;
  836.                   while (pmlst != NULL)
  837.                     {pflst = pmlst->pflstFirst;
  838.                      while (pflst != NULL)
  839.                        {if ((iselix == pflst->selv)
  840.                         && (offv >= pflst->offv)
  841.                         && (offv < (pflst->offv + pflst->lenv)))
  842.                           {sprintf (szFunction, "%s+%04X",
  843.                              pflst->pszName,
  844.                              offv - pflst->offv);
  845.                            goto OutputCount;
  846.                           }
  847.                         pflst = pflst->pflstNext;
  848.                        }
  849.                      pmlst = pmlst->pmlstNext;
  850.                     }
  851.  
  852.                // Output bucket count
  853.  
  854.                   OutputCount:
  855.  
  856.                   fprintf (fResults, 
  857.                            "%s, %04X:%04X, %ld, %s, %s, %ld\n",
  858.                     pbckt->pmlst->pszName,
  859.                     pbckt->segv,
  860.                     offv,
  861.                     pbckt->alBucket[iBucket],
  862.                     pbckt->pmlst->pszName,
  863.                     szFunction,
  864.                     pbckt->alBucket[iBucket]);
  865.                  }
  866.               }
  867.            }
  868.         }
  869.  
  870.       fclose (fResults);  // Close results file
  871. }
  872.  
  873. // Profile Requested Program
  874.  
  875. USHORT NEAR ProfileProgram (pszPgm)
  876.  
  877.       PCHAR pszPgm;             // -->Name of program to profile
  878.  
  879.    // Define function data
  880.  
  881.      {USHORT selv;              // Selector value
  882.       CHAR szDLL[CCHMAXPATH];   // Name of DLL being loaded
  883.       PCHAR pszDLL = szDLL;     // -->DLL name
  884.       PCHAR pszName;            // -->Module name
  885.       PCHAR pszTime;            // -->Current time string
  886.       time_t lTime;             // Current time and date
  887.  
  888.    // Initialize profiling
  889.  
  890.       glbl.ptb.cmd = PTRACE_CMD_STOP;
  891.       glbl.ptb.mte = 0;
  892.       DosPTrace ((PBYTE) &glbl.ptb);
  893.       time (&lTime); pszTime = ctime (&lTime); pszTime[24] = 0;
  894.       printf ("\n%s Starting %s", pszTime, pszPgm);
  895.  
  896.    // Build module list entry for .EXE
  897.  
  898.       BuildModuleList (pszPgm, FALSE);
  899.  
  900.    // Profile analysis loop
  901.  
  902.       while (TRUE)
  903.  
  904.       // Restart program after event
  905.  
  906.         {glbl.ptb.tid = 0;
  907.          glbl.ptb.cmd = PTRACE_CMD_GO;
  908.          DosPTrace ((PBYTE) &glbl.ptb);
  909.  
  910.       // Analyze event code
  911.  
  912.          switch ((SHORT) glbl.ptb.cmd)
  913.  
  914.          // Signal
  915.  
  916.            {case PTRACE_EVENT_SIGNAL:
  917.  
  918.             // Get CS:IP
  919.  
  920.                glbl.ptb.cmd = PTRACE_CMD_READ_REGISTERS;
  921.                DosPTrace ((PBYTE) &glbl.ptb);
  922.  
  923.             // Convert CS to an index [0..8191]
  924.  
  925.                glbl.ptb.rCS >>= 3;
  926.  
  927.             // Increment accumulator bucket. If we can 
  928.             // attribute the CS:IP, we increment both the 
  929.             // accumulator bucket and the total count of 
  930.             // "known" hits. Otherwise, we just increment 
  931.             // the total count of "unknown" hits.
  932.  
  933.                if (glbl.pselix->apbckt[glbl.ptb.rCS] != 0)
  934.                  {++(glbl.pselix->apbckt[glbl.ptb.rCS]->
  935.                     alBucket[glbl.ptb.rIP/glbl.cbBucket]);
  936.                   ++glbl.lKnown;
  937.                  }
  938.                else ++glbl.lUnknown;
  939.                break;
  940.  
  941.          // Process termination
  942.  
  943.             case PTRACE_EVENT_DYING:
  944.                time (&lTime); 
  945.                pszTime = ctime (&lTime); 
  946.                pszTime[24] = 0;
  947.                printf ("\n%s Terminating %s", pszTime, pszPgm);
  948.                return 0;
  949.  
  950.          // GP fault
  951.  
  952.             case PTRACE_EVENT_GP_FAULT:
  953.  
  954.             // If possible, convert CS:IP to 
  955.             // SEG:OFF in a known .EXE or .DLL
  956.  
  957.                selv = glbl.ptb.segv >> 3;
  958.                if (glbl.pselix->apbckt[selv] != NULL)
  959.                  {glbl.ptb.segv = 
  960.                       glbl.pselix->apbckt[selv]->segv;
  961.                   pszName = 
  962.                       glbl.pselix->apbckt[selv]->pmlst->pszName;
  963.                  }
  964.                else pszName = pszPgm;
  965.  
  966.             // Log GP fault
  967.  
  968.                time (&lTime); 
  969.                pszTime = ctime (&lTime); 
  970.                pszTime[24] = 0;
  971.                printf ("\n%s GP fault %04X in %s at %04X:%04X",
  972.                  pszTime, glbl.ptb.value, pszName,
  973.                  glbl.ptb.segv, glbl.ptb.offv);
  974.                return glbl.ptb.value;
  975.  
  976.          // Library load
  977.  
  978.             case PTRACE_EVENT_LIBRARY_LOAD:
  979.  
  980.             // Determine library name
  981.  
  982.                glbl.ptb.cmd = PTRACE_CMD_GET_LIBNAME;
  983.                glbl.ptb.segv = FP_SEG (pszDLL);
  984.                glbl.ptb.offv = FP_OFF (pszDLL);
  985.                DosPTrace ((PBYTE) &glbl.ptb);
  986.                glbl.ptb.mte = glbl.ptb.value;
  987.  
  988.             // Build module list entry for .DLL
  989.  
  990.                BuildModuleList (strupr (pszDLL), TRUE);
  991.  
  992.             // Log library load
  993.  
  994.                time (&lTime); 
  995.                pszTime = ctime (&lTime); 
  996.                pszTime[24] = 0;
  997.                printf ("\n%s Loading %s", pszTime, szDLL);
  998.                break;
  999.  
  1000.          // Thread termination
  1001.  
  1002.             case PTRACE_EVENT_THREAD_DEAD:
  1003.                time (&lTime); 
  1004.                pszTime = ctime (&lTime); 
  1005.                pszTime[24] = 0;
  1006.                printf ("\n%s Terminating thread %u in %s",
  1007.                  pszTime, glbl.ptb.tid, pszPgm);
  1008.                break;
  1009.  
  1010.          // DosPTrace Error
  1011.  
  1012.             case PTRACE_EVENT_ERROR:
  1013.                time (&lTime); 
  1014.                pszTime = ctime (&lTime); 
  1015.                pszTime[24] = 0;
  1016.                printf ("\n%s DosPtrace error %u in %s",
  1017.                  pszTime, glbl.ptb.value, pszPgm);
  1018.                return glbl.ptb.value;
  1019.  
  1020.          // Ignore others
  1021.             break:
  1022.                return;
  1023.            }
  1024.  
  1025.       // Restart program until it hurts!
  1026.  
  1027.         }
  1028.  
  1029.    // End of profiling
  1030. }
  1031.  
  1032. // Thread to Signal Profiled Program
  1033.  
  1034. VOID FAR Signaller (pCB)
  1035.  
  1036.       PVOID pCB;                // Dummy control block
  1037.  
  1038.    // Define function data
  1039.  
  1040. {     USHORT idErr;             // Error code
  1041.  
  1042.    // Signal profiled program and wait for the sample interval
  1043.  
  1044.       while (TRUE)
  1045.        {idErr = DosFlagProcess (glbl.ptb.pid, 
  1046.                                 FLGP_PID, PFLG_A, 0);
  1047.         DosSleep (SAMPLE_INTERVAL);
  1048.        }
  1049. }  // Terminate thread
  1050.  
  1051. // Start Program to be Profiled
  1052.  
  1053. VOID NEAR StartProgram (argc, argv, iarg, pszPgm)
  1054.  
  1055.       int argc;                 // Argument count
  1056.       char *argv[];             // Argument values
  1057.       int iarg;                 // Index to program name
  1058.       PCHAR pszPgm;             // -->Area program name returned
  1059.  
  1060.    // Define function data
  1061.  
  1062. {     STARTDATA stdata;         // DosStartSession data
  1063.       USHORT idErr;             // Error code
  1064.       CHAR szArgs[CCHMAXPATH];  // Program arguments
  1065.       USHORT idSession;         // Session id
  1066.  
  1067.    // Extract program name from arguments
  1068.  
  1069.       strcpy (pszPgm, argv[iarg]);
  1070.       strupr (pszPgm);
  1071.       if (strstr (pszPgm, ".EXE") == NULL)
  1072.         strcat (pszPgm, ".EXE");
  1073.  
  1074.    // Build argument string for program to be profiled
  1075.  
  1076.       szArgs[0] = 0;
  1077.       for (iarg++; iarg < argc; iarg++)
  1078.         {if ((strlen (szArgs) + 
  1079.               strlen (argv[iarg]) + 1) >= sizeof (szArgs))
  1080.             break;
  1081.          strcat (szArgs, argv[iarg]);
  1082.          strcat (szArgs, " ");
  1083.         }
  1084.  
  1085.    // Start program being profiled
  1086.  
  1087.       stdata.Length = sizeof (stdata);
  1088.       stdata.Related = TRUE;
  1089.       stdata.FgBg = FALSE;
  1090.       stdata.TraceOpt = TRUE;
  1091.       stdata.PgmTitle = NULL;
  1092.       stdata.PgmName = pszPgm;
  1093.       stdata.PgmInputs = szArgs;
  1094.       stdata.TermQ = NULL;
  1095.       stdata.Environment = NULL;
  1096.       stdata.InheritOpt = TRUE;
  1097.       idErr = DosQAppType (pszPgm, &stdata.SessionType);
  1098.       stdata.IconFile = NULL;
  1099.       stdata.PgmHandle = (ULONG) NULL;
  1100.       stdata.PgmControl = 2;
  1101.       stdata.InitXPos = stdata.InitYPos = 0;
  1102.       stdata.InitXSize = stdata.InitYSize = 0;
  1103.       idErr = DosStartSession (
  1104.                  &stdata, &idSession, &glbl.ptb.pid);
  1105.  
  1106.    // Issue an error if the program could not be started
  1107.  
  1108.       if (idErr != 0)
  1109.         {printf ("\n%s could not be started (error %u)\n", 
  1110.                    pszPgm, idErr);
  1111.          exit (idErr);
  1112.         }
  1113. }